#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
MCP决策优化数据模型
定义决策优化相关的数据结构和枚举类型
"""

from enum import Enum
from typing import Dict, List, Any, Optional, Union
from dataclasses import dataclass
from datetime import datetime

# ============================================================================
# 枚举类型定义
# ============================================================================

class VariableType(Enum):
    """决策变量类型"""
    CONTINUOUS = "continuous"  # 连续变量
    INTEGER = "integer"        # 整数变量
    BINARY = "binary"          # 二进制变量
    CATEGORICAL = "categorical" # 分类变量

class OptimizationType(Enum):
    """优化类型"""
    MINIMIZE = "minimize"  # 最小化
    MAXIMIZE = "maximize"  # 最大化

class ConstraintType(Enum):
    """约束类型"""
    EQUALITY = "equality"        # 等式约束
    INEQUALITY = "inequality"    # 不等式约束
    BOUND = "bound"              # 边界约束
    LINEAR = "linear"            # 线性约束
    NONLINEAR = "nonlinear"      # 非线性约束

class UncertaintyType(Enum):
    """不确定性类型"""
    NORMAL = "normal"            # 正态分布
    UNIFORM = "uniform"          # 均匀分布
    TRIANGULAR = "triangular"    # 三角分布
    EXPONENTIAL = "exponential"  # 指数分布
    SCENARIO = "scenario"        # 情景分析

class AlgorithmType(Enum):
    """算法类型"""
    GENETIC_ALGORITHM = "genetic_algorithm"      # 遗传算法
    PARTICLE_SWARM = "particle_swarm"            # 粒子群算法
    SIMULATED_ANNEALING = "simulated_annealing"  # 模拟退火
    GRADIENT_DESCENT = "gradient_descent"        # 梯度下降
    NEWTON_METHOD = "newton_method"              # 牛顿法
    SIMPLEX = "simplex"                          # 单纯形法
    BRANCH_AND_BOUND = "branch_and_bound"        # 分支定界法
    NSGA_II = "nsga_ii"                          # NSGA-II多目标算法
    MOEA_D = "moea_d"                            # MOEA/D多目标算法

# ============================================================================
# 基础数据模型
# ============================================================================

@dataclass
class DecisionVariable:
    """决策变量模型"""
    name: str
    variable_type: VariableType
    lower_bound: Optional[float] = None
    upper_bound: Optional[float] = None
    initial_value: Optional[float] = None
    description: str = ""
    categories: Optional[List[str]] = None  # 分类变量的可选值
    
    def __post_init__(self):
        """数据验证"""
        if self.variable_type == VariableType.BINARY:
            self.lower_bound = 0
            self.upper_bound = 1
        elif self.variable_type == VariableType.CATEGORICAL and not self.categories:
            raise ValueError("分类变量必须提供categories参数")

@dataclass
class ObjectiveFunction:
    """目标函数模型"""
    name: str
    optimization_type: OptimizationType
    expression: str = ""
    weight: float = 1.0
    description: str = ""
    
    def __post_init__(self):
        """数据验证"""
        if self.weight <= 0:
            raise ValueError("目标函数权重必须大于0")

@dataclass
class Constraint:
    """约束条件模型"""
    name: str
    constraint_type: ConstraintType
    expression: str = ""
    rhs_value: float = 0.0  # 右侧值
    tolerance: float = 1e-6
    description: str = ""
    
    def __post_init__(self):
        """数据验证"""
        if self.tolerance < 0:
            raise ValueError("容差必须非负")

@dataclass
class UncertaintyParameter:
    """不确定性参数模型"""
    name: str
    uncertainty_type: UncertaintyType
    parameters: Dict[str, float]  # 分布参数
    description: str = ""
    
    def __post_init__(self):
        """数据验证"""
        required_params = {
            UncertaintyType.NORMAL: ['mean', 'std'],
            UncertaintyType.UNIFORM: ['min', 'max'],
            UncertaintyType.TRIANGULAR: ['min', 'mode', 'max'],
            UncertaintyType.EXPONENTIAL: ['rate']
        }
        
        if self.uncertainty_type in required_params:
            for param in required_params[self.uncertainty_type]:
                if param not in self.parameters:
                    raise ValueError(f"{self.uncertainty_type.value}分布缺少参数: {param}")

@dataclass
class OptimizationProblem:
    """优化问题模型"""
    name: str
    variables: List[DecisionVariable]
    objectives: List[ObjectiveFunction]
    constraints: List[Constraint] = None
    uncertainty_params: List[UncertaintyParameter] = None
    description: str = ""
    created_at: datetime = None
    
    def __post_init__(self):
        """数据验证和初始化"""
        if self.constraints is None:
            self.constraints = []
        if self.uncertainty_params is None:
            self.uncertainty_params = []
        if self.created_at is None:
            self.created_at = datetime.now()
        
        if not self.variables:
            raise ValueError("优化问题必须包含至少一个决策变量")
        if not self.objectives:
            raise ValueError("优化问题必须包含至少一个目标函数")

@dataclass
class AlgorithmConfig:
    """算法配置模型"""
    name: str
    algorithm_type: AlgorithmType
    parameters: Dict[str, Any]
    description: str = ""
    
    def __post_init__(self):
        """参数验证"""
        # 验证常见算法参数
        if self.algorithm_type == AlgorithmType.GENETIC_ALGORITHM:
            required = ['population_size', 'generations']
            for param in required:
                if param not in self.parameters:
                    raise ValueError(f"遗传算法缺少参数: {param}")
        elif self.algorithm_type == AlgorithmType.PARTICLE_SWARM:
            required = ['swarm_size', 'max_iterations']
            for param in required:
                if param not in self.parameters:
                    raise ValueError(f"粒子群算法缺少参数: {param}")

@dataclass
class ParetoSolution:
    """帕累托解模型"""
    solution: Dict[str, float]  # 变量名 -> 值
    objective_values: Dict[str, float]  # 目标函数名 -> 值
    rank: int = 0  # 非支配排序等级
    crowding_distance: float = 0.0  # 拥挤距离
    
    def dominates(self, other: 'ParetoSolution') -> bool:
        """判断是否支配另一个解"""
        better_in_any = False
        for obj_name, obj_value in self.objective_values.items():
            other_value = other.objective_values.get(obj_name, float('inf'))
            if obj_value > other_value:  # 假设最大化
                return False
            elif obj_value < other_value:
                better_in_any = True
        return better_in_any

@dataclass
class MultiObjectiveResult:
    """多目标优化结果模型"""
    problem_name: str
    algorithm_type: AlgorithmType
    pareto_front: List[ParetoSolution]  # 帕累托前沿解集
    execution_time: float
    iterations: int
    convergence_metrics: Dict[str, float] = None  # 收敛指标
    hypervolume: float = 0.0  # 超体积指标
    spacing: float = 0.0  # 间距指标
    metadata: Dict[str, Any] = None
    created_at: datetime = None
    
    def __post_init__(self):
        """初始化"""
        if self.convergence_metrics is None:
            self.convergence_metrics = {}
        if self.metadata is None:
            self.metadata = {}
        if self.created_at is None:
            self.created_at = datetime.now()
    
    def get_best_solutions(self, n: int = 5) -> List[ParetoSolution]:
        """获取前n个最优解"""
        sorted_solutions = sorted(self.pareto_front, key=lambda x: (x.rank, -x.crowding_distance))
        return sorted_solutions[:n]

@dataclass
class OptimizationResult:
    """优化结果模型"""
    problem_name: str
    algorithm_type: AlgorithmType
    solution: Dict[str, float]  # 变量名 -> 最优值
    objective_value: Union[float, Dict[str, float]]  # 单目标或多目标值
    execution_time: float
    iterations: int
    convergence_info: Dict[str, Any] = None
    metadata: Dict[str, Any] = None
    created_at: datetime = None
    
    def __post_init__(self):
        """初始化"""
        if self.convergence_info is None:
            self.convergence_info = {}
        if self.metadata is None:
            self.metadata = {}
        if self.created_at is None:
            self.created_at = datetime.now()

@dataclass
class RiskProfile:
    """风险概况模型"""
    var_95: float  # 95%置信度的风险价值
    var_99: float  # 99%置信度的风险价值
    cvar_95: float  # 95%置信度的条件风险价值
    cvar_99: float  # 99%置信度的条件风险价值
    expected_value: float  # 期望值
    standard_deviation: float  # 标准差
    skewness: float  # 偏度
    kurtosis: float  # 峰度
    confidence_intervals: Dict[str, tuple]  # 置信区间 {"95%": (lower, upper)}
    
    def get_risk_level(self) -> str:
        """评估风险等级"""
        cv = abs(self.standard_deviation / self.expected_value) if self.expected_value != 0 else float('inf')
        if cv < 0.1:
            return "低风险"
        elif cv < 0.3:
            return "中等风险"
        else:
            return "高风险"

@dataclass
class ScenarioAnalysis:
    """情景分析模型"""
    scenario_name: str
    probability: float  # 情景发生概率
    objective_values: Dict[str, float]  # 各目标函数在该情景下的值
    solution: Dict[str, float]  # 该情景下的最优解
    risk_metrics: Dict[str, float]  # 风险指标
    description: str = ""
    
    def __post_init__(self):
        """验证概率值"""
        if not 0 <= self.probability <= 1:
            raise ValueError("概率值必须在0到1之间")

@dataclass
class MonteCarloResult:
    """蒙特卡洛模拟结果模型"""
    simulation_count: int  # 模拟次数
    objective_samples: List[float]  # 目标函数值样本
    solution_samples: List[Dict[str, float]]  # 解的样本
    risk_profile: RiskProfile  # 风险概况
    convergence_analysis: Dict[str, Any]  # 收敛性分析
    execution_time: float  # 执行时间
    random_seed: Optional[int] = None  # 随机种子
    created_at: datetime = None
    
    def __post_init__(self):
        """初始化时间戳"""
        if self.created_at is None:
            self.created_at = datetime.now()
    
    def get_percentile(self, percentile: float) -> float:
        """获取指定百分位数"""
        import numpy as np
        return np.percentile(self.objective_samples, percentile)
    
    def get_probability_above(self, threshold: float) -> float:
        """计算超过阈值的概率"""
        count = sum(1 for x in self.objective_samples if x > threshold)
        return count / len(self.objective_samples)

@dataclass
class RobustSolution:
    """鲁棒优化解模型"""
    solution: Dict[str, float]  # 鲁棒解
    worst_case_objective: float  # 最坏情况下的目标函数值
    best_case_objective: float  # 最好情况下的目标函数值
    expected_objective: float  # 期望目标函数值
    robustness_measure: float  # 鲁棒性度量
    scenario_performance: List[ScenarioAnalysis]  # 各情景下的表现
    uncertainty_budget: Dict[str, float]  # 不确定性预算
    sensitivity_analysis: Dict[str, float]  # 敏感性分析
    
    def get_regret(self, scenario: ScenarioAnalysis) -> float:
        """计算在特定情景下的后悔值"""
        optimal_in_scenario = scenario.objective_values.get('optimal', float('inf'))
        actual_performance = scenario.objective_values.get('actual', self.expected_objective)
        return abs(optimal_in_scenario - actual_performance)
    
    def get_robustness_score(self) -> float:
        """计算鲁棒性评分(0-100)"""
        if self.best_case_objective == self.worst_case_objective:
            return 100.0
        range_ratio = (self.expected_objective - self.worst_case_objective) / (self.best_case_objective - self.worst_case_objective)
        return max(0, min(100, range_ratio * 100))

@dataclass
class UncertaintyResult:
    """不确定性分析结果模型"""
    problem_name: str
    analysis_type: str  # "monte_carlo", "robust_optimization", "scenario_analysis"
    monte_carlo_result: Optional[MonteCarloResult] = None
    robust_solution: Optional[RobustSolution] = None
    scenario_analyses: List[ScenarioAnalysis] = None
    uncertainty_impact: Dict[str, float] = None  # 不确定性对各目标的影响
    decision_recommendations: List[str] = None  # 决策建议
    execution_time: float = 0.0
    created_at: datetime = None
    
    def __post_init__(self):
        """初始化默认值"""
        if self.created_at is None:
            self.created_at = datetime.now()
        if self.scenario_analyses is None:
            self.scenario_analyses = []
        if self.uncertainty_impact is None:
            self.uncertainty_impact = {}
        if self.decision_recommendations is None:
            self.decision_recommendations = []
    
    def get_overall_risk_level(self) -> str:
        """获取整体风险等级"""
        if self.monte_carlo_result and self.monte_carlo_result.risk_profile:
            return self.monte_carlo_result.risk_profile.get_risk_level()
        elif self.robust_solution:
            score = self.robust_solution.get_robustness_score()
            if score >= 80:
                return "低风险"
            elif score >= 60:
                return "中等风险"
            else:
                return "高风险"
        return "未知风险"
    
    def generate_summary_report(self) -> Dict[str, Any]:
        """生成摘要报告"""
        report = {
            "problem_name": self.problem_name,
            "analysis_type": self.analysis_type,
            "overall_risk_level": self.get_overall_risk_level(),
            "execution_time": self.execution_time,
            "created_at": self.created_at.isoformat() if self.created_at else None
        }
        
        if self.monte_carlo_result:
            report["monte_carlo_summary"] = {
                "simulation_count": self.monte_carlo_result.simulation_count,
                "expected_value": self.monte_carlo_result.risk_profile.expected_value,
                "standard_deviation": self.monte_carlo_result.risk_profile.standard_deviation,
                "var_95": self.monte_carlo_result.risk_profile.var_95
            }
        
        if self.robust_solution:
            report["robust_solution_summary"] = {
                "robustness_score": self.robust_solution.get_robustness_score(),
                "worst_case_objective": self.robust_solution.worst_case_objective,
                "expected_objective": self.robust_solution.expected_objective
            }
        
        if self.scenario_analyses:
            report["scenario_count"] = len(self.scenario_analyses)
            report["scenario_probabilities"] = [s.probability for s in self.scenario_analyses]
        
        return report

# ============================================================================
# 辅助函数
# ============================================================================

def validate_variable_bounds(variable: DecisionVariable) -> bool:
    """验证变量边界"""
    if variable.lower_bound is not None and variable.upper_bound is not None:
        return variable.lower_bound <= variable.upper_bound
    return True

def get_variable_range(variable: DecisionVariable) -> tuple:
    """获取变量取值范围"""
    if variable.variable_type == VariableType.BINARY:
        return (0, 1)
    elif variable.variable_type == VariableType.CATEGORICAL:
        return (0, len(variable.categories) - 1)
    else:
        return (variable.lower_bound, variable.upper_bound)

def is_multi_objective(problem: OptimizationProblem) -> bool:
    """判断是否为多目标优化问题"""
    return len(problem.objectives) > 1

def get_problem_complexity(problem: OptimizationProblem) -> str:
    """评估问题复杂度"""
    var_count = len(problem.variables)
    obj_count = len(problem.objectives)
    const_count = len(problem.constraints)
    
    if var_count <= 10 and obj_count == 1 and const_count <= 5:
        return "简单"
    elif var_count <= 50 and obj_count <= 3 and const_count <= 20:
        return "中等"
    else:
        return "复杂"

# ============================================================================
# 导出所有模型
# ============================================================================

__all__ = [
    # 枚举类型
    'VariableType', 'OptimizationType', 'ConstraintType', 
    'UncertaintyType', 'AlgorithmType',
    
    # 数据模型
    'DecisionVariable', 'ObjectiveFunction', 'Constraint',
    'UncertaintyParameter', 'OptimizationProblem', 'AlgorithmConfig',
    'OptimizationResult', 'ParetoSolution', 'MultiObjectiveResult',
    'RiskProfile', 'ScenarioAnalysis', 'MonteCarloResult',
    'RobustSolution', 'UncertaintyResult',
    
    # 辅助函数
    'validate_variable_bounds', 'get_variable_range', 'is_multi_objective',
    'get_problem_complexity'
]